
    /**  
    * @Title: CheckTableSize.java
    * @Package com.frames.table.separator
    * @Description: 
    * @author likai
    * @date 2017年4月28日
    * @version V1.0  
    */
    
package com.frames.table.separator;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;

import javax.annotation.Resource;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.supers.enums.TableDml;
import com.utils.validate.RegexUtil;

/**
    * @ClassName: CheckTableSize
    * @Description: 1.检测数据库每张表的数据量,2.主表用t_user,分页表用t_user_1,t_user_2等形式生成
    * 				3.将此切面拷贝到项目中
    * @author likai
    * @date 2017年4月28日
    *
    */
public class CheckTableSize {
	
	public static Logger log = LoggerFactory.getLogger(CheckTableSize.class);

	/*需配置参数开始*/
	private   String database="test";//获取表的数据库名,根据项目配置一个主库就行
	private   Integer rowSize=10;//每张表允许一百万条数据
	/*需配置参数结束*/
	
	public static Hashtable<String,Integer> tableNum=new Hashtable<String,Integer>();//最新分页表名及表序号
	public static Hashtable<String,Integer> tableSize=new Hashtable<String,Integer>();//最新分页表名及数据总数
	
	@Resource
	private TableDao tableDao;


	@SuppressWarnings("rawtypes")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
		log.info("---------------------分库分表策略开始----------------------");
		initTable();//初始化新表信息
		Object retVal = null;
		Object[] args=pjp.getArgs();
		Signature s = pjp.getSignature();
		MethodSignature ms = (MethodSignature)s;
		Method m = ms.getMethod();
		IsInsert isInsert=m.getAnnotation(IsInsert.class);		
		if (isInsert!=null) {	
			//增删改操作
			int datanum=1;//操作数据数量,默认一条
			String tableName=isInsert.tableName();
			TableDml dml=isInsert.dml();//增删改			
			try {
				boolean batch=isInsert.batch();//是否批量操作
				if (batch) {
					//批量操作获取操作数据数量
					for (int i = 1; i < args.length; i++) {
						if (args[i] instanceof Collection) {
							Collection data=(Collection) args[i];
							datanum=data.size();
						}
						if (args[i] instanceof Object[]) {
							Object[] data=(Object[]) args[i];
							datanum=data.length;
						}
					}
				} 
				updateTable(tableName, dml,datanum);
				if (!isInsert.dml().equals(TableDml.INSERT)) {					
					args[0]=new int[tableNum.get(tableName)+1];//给更新和删除操作设置拦截方法的第一个参数，表序号				
				}
				retVal = pjp.proceed(args);
			} catch (Throwable e) {
				//出错，数据还原
				log.info(e.getMessage());
				backTable(tableName, dml,datanum);
				throw new RuntimeException(tableName+":增删数据失败");
			}
		}else {		
			//查询操作
			retVal = pjp.proceed();
		}
		
		log.info("---------------------分库分表策略结束----------------------");
        return retVal;
    }

	
	//集合数据还原
	synchronized private void backTable(String tableName,TableDml dml ,int datanum ) {
		if (dml.equals(TableDml.INSERT)) {
			//添加数据,记录减datanum
			tableSize.put(tableName, tableSize.get(tableName)-datanum);
		} else if (dml.equals(TableDml.DELETE)) {			
			//删除数据,记录加datanum
			tableSize.put(tableName, tableSize.get(tableName)+datanum);
		}
	}
	private  String getCreateTableSql(String ddl,String mainTable,int tableIndex) {
		String unionTables="";
		for (int i = 0; i <= tableIndex; i++) {
			if (i==0) {				
				unionTables+=mainTable+",";
			}else {
				unionTables+=mainTable+"_"+i+",";
			}
		}
		unionTables=unionTables.substring(0,unionTables.length()-1);
		ddl=ddl.replace(mainTable, mainTable+"_union");
		ddl=ddl.replace("InnoDB", "MRG_MyISAM");
		ddl=ddl.replace("MyISAM", "MRG_MyISAM");
		ddl+=" UNION=("+unionTables+") INSERT_METHOD=LAST ";
		return ddl;
	}
	//初始化表信息数据
	synchronized private void initTable() {
		if (tableSize.size()==0) {
			//数据库表数据未加载，加载新表信息		
			String tableItem="";
			TableInfo tableInfo=new TableInfo();
			//数据库表数据未加载，则加载数据库表信息
			tableInfo.setDatabase(database);
			List<TableInfo> tables= tableDao.listTables(tableInfo);
			for (TableInfo item : tables) {
				String table=item.getTableName();
				Integer rowNum=item.getRowNum();
				if (!table.contains("_union")) {					
					int tableIndex=RegexUtil.isEndByNum(table);
					if (tableIndex!=-1) {
						//是分页表,获取表名
						tableItem=table.replace("_"+tableIndex, "");
					}else {
						//主表
						tableIndex=0;
						tableItem=table;					

					}
					//更新当前表对应的最新分页表和表的数据记录数
					if (tableNum.get(tableItem)!=null) {
						if (tableNum.get(tableItem)<tableIndex) {
							tableNum.put(tableItem,tableIndex);
							tableSize.put(tableItem, rowNum);
						}
					}else {
						tableNum.put(tableItem,tableIndex);
						tableSize.put(tableItem, rowNum);
					}
				}
			}
		} 
		
	}

	//增删改时更新表数据信息
	synchronized private  void updateTable(String tableName,TableDml dml,int datanum) {
		//数据库表数据已加载，更新表信息
		if (dml.equals(TableDml.INSERT)) {
			//添加数据,记录加datanum
			tableSize.put(tableName, tableSize.get(tableName)+datanum);
		} else if (dml.equals(TableDml.DELETE)) {				
			//删除数据,记录减datanum
			tableSize.put(tableName, tableSize.get(tableName)-datanum);
		}

		if (tableSize.get(tableName)>rowSize) {
			//数据超出每张表最大记录数，创建新表
			TableInfo tableInfo=new TableInfo();
			int lastTableIndex=tableNum.get(tableName);
			tableInfo.setTableName(tableName+(lastTableIndex==0?"":("_"+lastTableIndex)));
			log.info("lastTableIndex:"+tableInfo.toString());
			long maxId=tableDao.getTableMaxId(tableInfo).get("maxId").longValue();//获取所有表最大id
			int tableIndex=lastTableIndex+1;//当前表序号
			String table=tableName+"_"+tableIndex;//获取新建表的表名
			tableInfo.setStartIndex(maxId);
			tableInfo.setTableName(table);
			tableInfo.setMainTable(tableName);
			String tableDDL=tableDao.getTableDDL(tableInfo).get("Create Table");//获取table的ddl信息
			String createSql=getCreateTableSql(tableDDL, tableName, tableIndex);
			tableInfo.setTableDDl(createSql);
			tableInfo.setUnionTable(tableName+"_union");
			tableDao.createPageTable(tableInfo);//创建新表，id从maxId开始
			
			//更新当前tableNum和tableSize
			tableNum.put(tableName, tableIndex);
			tableSize.put(tableName, datanum);
		}
		
	}

	
	//根据表名获取查询的表
	public static String getQueryTable(String tableName) {
		Integer num=tableNum.get(tableName);
		if (num!=1) {
			tableName=tableName+"_union";
		}
		return tableName;
	}
	//根据表名获取最新表
	public static String getLastTable(String tableName) {
		Integer num=tableNum.get(tableName);
		if (num!=1) {
			tableName=tableName+"_"+num;
		}
		return tableName;
	}

	public String getDatabase() {
		return database;
	}


	public void setDatabase(String database) {
		this.database = database;
	}


	public Integer getRowSize() {
		return rowSize;
	}


	public void setRowSize(Integer rowSize) {
		this.rowSize = rowSize;
	}
	
	
	
	

	
	
}
